<template>
  <div class="arcDiagram" id="arcDiagramNode"></div>
</template>

<script setup lang="ts">
import G6 from '@antv/g6';
import { onMounted, defineExpose, nextTick } from 'vue';
import { Call, Node, QueryServiceTopo } from '/@/api/maintenance-center/skywalking/topology/model/topologyModel';
import { getServicesTopology } from '/@/api/maintenance-center/skywalking/topology/topology';
import { DurationDto } from '/@/api/maintenance-center/skywalking/common/model/commonModel';

const props = defineProps(['serviceIds', 'start', 'end', 'step']);

const colors = [
  'rgb(91, 143, 249)',
  'rgb(90, 216, 166)',
  'rgb(93, 112, 146)',
  'rgb(246, 189, 22)',
  'rgb(232, 104, 74)',
  'rgb(109, 200, 236)',
  'rgb(146, 112, 202)',
  'rgb(255, 157, 77)',
  'rgb(38, 154, 153)',
  'rgb(227, 137, 163)',
];

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const container = document.getElementById('arcDiagramNode');
const width = 1700;
const height = 760;

let nodes: Node[] = [];
let calls: Call[] = [];

let durationDto: DurationDto;

var graph;

// 定义数据源
const data = {
  // 点集
  nodes: [],
  // 边集
  edges: [],
};

// 定义节点提示框
const nodeTooltip = new G6.Tooltip({
  offsetX: 15,
  offsety: -20,
  getContent(e) {
    const outDiv = document.createElement('div');
    let nodeName = e.item.getModel().name;

    outDiv.style.width = 'fit-content';
    outDiv.style.height = '18px';
    outDiv.innerHTML = `
    <ul id="edgeRelation">
      <li>服务：${nodeName}</li>
    </ul>`;
    return outDiv;
  },
  itemTypes: ['node'],
});

// 定义边提示框
const edgeTooltip = new G6.Tooltip({
  offsetX: 15,
  offsety: -20,
  getContent(e) {
    const outDiv = document.createElement('div');
    let edgeSourceId = e.item.getModel().source;
    let edgeTargetId = e.item.getModel().target;

    let sourceNode = nodes.find((item) => {
      return item.id === edgeSourceId;
    });
    let targetNode = nodes.find((item) => {
      return item.id === edgeTargetId;
    });

    outDiv.style.width = 'fit-content';
    outDiv.style.height = '18px';
    outDiv.innerHTML = `
    <ul id="edgeRelation">
      <li>${sourceNode.name} > ${targetNode.name}</li>
    </ul>`;
    return outDiv;
  },
  itemTypes: ['edge'],
});
function ifDateUndefined(start: string, end: string, step: string) {
  let isStartUndefined = start == '' || start === null || start == undefined;
  let isEndUndefined = end == '' || end === null || end == undefined;
  let isStepUndefined = step == '' || step === null || step == undefined;
  if (isStartUndefined || isEndUndefined || isStepUndefined) {
    return true;
  }
  return false;
}
const traverseNode = (nodeParams) => {
  let { nodes, angleSep, radius, nodeMap, nodeTypeMap, nodeTypeId } = nodeParams;
  nodes.forEach((node, i) => {
    node.label = node.name;
    node.nodeType = node.type;

    let angle = i * angleSep;
    node.x = radius * Math.cos(angle) + origin[0];
    node.y = radius * Math.sin(angle) + origin[1];
    node.angle = angle;

    nodeMap.set(node.id, node);

    // nodeType
    if (node.nodeType && nodeTypeMap.get(node.nodeType) === undefined) {
      nodeTypeMap.set(node.nodeType, nodeTypeId);
      nodeTypeId++;
    }

    let id = nodeTypeMap.get(node.nodeType);
    if (node.style) {
      node.style.fill = colors[id % colors.length];
    } else {
      node.style = {
        fill: colors[id % colors.length],
      };
    }

    // label
    node.labelCfg = {
      position: 'center',
      style: {
        rotate: angle,
        rotateCenter: 'lefttop',
        textAlign: 'start',
      },
    };
  });
};

const fetchData = async (queryServiceTopo) => {
  const res = await getServicesTopology(queryServiceTopo);

  let origin = [width / 2, height / 2];
  let radius = width < height ? width / 5 : height / 5;

  let nodeMap = new Map();
  let nodeTypeMap = new Map();

  let nodeTypeId = 0;
  let n = res.nodes.length;
  let angleSep = (Math.PI * 2) / n;

  nodes = res.nodes;
  calls = res.calls;
  let nodeParams = { nodes, angleSep, radius, nodeMap, nodeTypeMap, nodeTypeId };
  traverseNode(nodeParams);

  data.nodes = nodes;

  calls.forEach((edge) => {
    edge.type = 'quadratic';
    const source = nodeMap.get(edge.source);
    const target = nodeMap.get(edge.target);
    edge.controlPoints = [
      {
        x: origin[0],
        y: origin[1],
      },
    ];
    edge.color = source.style.fill;
    edge.sourceName = source.name;
    edge.targetName = target.name;
  });

  data.edges = calls;
};
async function renderArc() {
  await nextTick();
  let serviceIdList = props.serviceIds;
  let isServiceIdsUndefined = serviceIdList?.length === 0 || serviceIdList === null || serviceIdList == undefined;

  if (!isServiceIdsUndefined && !ifDateUndefined(props.start, props.end, props.step)) {
    durationDto = {
      start: props.start,
      end: props.end,
      step: props.step,
    };

    let queryServiceTopo: QueryServiceTopo = {
      duration: durationDto,
      serviceCodes: serviceIdList,
    };
    await fetchData(queryServiceTopo);

    // 更新数据源
    graph.changeData();
    graph.render();
  }
}
onMounted(async () => {
  // 创建 G6 图实例
  graph = new G6.Graph({
    container: 'arcDiagramNode', // 指定图画布的容器 id，与第 9 行的容器对应
    // 画布宽高
    fitCenter: true,
    width: width,
    height: height,
    linkCenter: true,
    plugins: [nodeTooltip, edgeTooltip],
    modes: {
      default: ['drag-canvas', 'zoom-canvas'],
    },
    fitView: true, // 是否开启画布自适应。开启后图自动适配画布大小。
    fitViewPadding: 100, // 画布的padding值
    defaultNode: {
      style: {
        opacity: 0.8,
        lineWidth: 1,
        stroke: '#999',
        cursor: 'pointer',
      },
    },
    defaultEdge: {
      size: 1,
      color: '#e2e2e2',
      style: {
        opacity: 0.6,
        lineAppendWidth: 3,
        cursor: 'pointer',
      },
    },
  });
  // 读取数据
  graph.data(data);

  graph.on('edge:mouseenter', (e) => {
    graph.setItemState(e.item, 'active', true);
  });
  graph.on('edge:mouseleave', (e) => {
    graph.setItemState(e.item, 'active', false);
  });
  renderArc();
});
defineExpose({ renderArc });
</script>

<style lang="less" scoped>
.arcDiagram {
  background: #e6ecff;
}

.service-detail {
  width: 400px;
  position: absolute;
  top: 70px;
  left: 10px;
}

.service-detail::-webkit-scrollbar {
  display: none; /* Chrome Safari */
}

.service-detail-right {
  width: 400px;
  position: absolute;
  top: 70px;
  right: 10px;

  height: 740px;
  overflow-y: scroll;
  scrollbar-width: none;
  -ms-overflow-style: none; /* IE 10+ */
}

.service-detail-right::-webkit-scrollbar {
  display: none; /* Chrome Safari */
}
</style>
